#include "srwithdrawcashauditwidget.h"

#include <thread>
#include <QSettings>
#include <QFile>
#include <QInputDialog>
#include <QTime>
#include <QTextCodec>
#include <QProcess>
#include <QTimer>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QPushButton>
#include <QCheckBox>
#include <QLineEdit>
#include <QLabel>
#include <QJsonDocument>
#include <QTableView>
#include <QHeaderView>
#include <QStandardItemModel>
#include <QFileDialog>
#include <QJsonObject>
#include <QJsonValue>
#include <QJsonArray>
#include <QMessageBox>
#include <QDebug>

#include <OEInputBox>
#include <OEMessageBox>
#include <OENotifyMessageBox>
#include <OEWalletManage>


/// private xls lib
#include <oeexcelreader.h>
#include <oeexcelwriter.h>

#include <auditcryptogram.h>

#include "srtransactionwidget.h"
#include "srdatabase.h"
#include "srdefine.h"


#define TABLE_IDX       (0)
#define TABLE_AUDIT_ID  (1)
#define TABLE_STATUS    (2)
#define TABLE_SUM       (3)
#define TABLE_OPERATION (4)
#define TABLE_COUNT     (5)

extern std::string BITCOIN_TYPE;



SRWithdrawCashAuditWidget::SRWithdrawCashAuditWidget(QWidget *parent) : QWidget(parent),
    bThreadReadExcel_(false),bThreadSign_(false)
{
    QVBoxLayout * layout = new QVBoxLayout();
    QHBoxLayout * layout_navigation = new QHBoxLayout();
    QHBoxLayout * layout_query = new QHBoxLayout();
    QHBoxLayout * layout_operator = new QHBoxLayout();
    QHBoxLayout * layout_bottom = new QHBoxLayout();

    QPushButton* btn_import = new QPushButton(tr("导入"));
    QPushButton* btn_signature = new QPushButton(tr("批量签名"));
    QPushButton* btn_export = new QPushButton(tr("导出"));
    btnAll_ = new QPushButton("全  选");
    pageAuditInfo_ = new SRTransactionWidget();
    QLabel* lab_audit_num = new QLabel(tr("审核编号:"));
    labWalletName_ = new QLabel(tr("当前钱包:     "));
    labWalletName_->setObjectName(tr("当前钱包: %1"));
    labAuditCount_ = new QLabel(tr("审核数: 0"));
    labAuditCount_->setObjectName(tr("审核数: %1"));
    edtAuditNum_ = new QLineEdit();
    QRegExp regQuote("[0-9a-zA-Z]{1,36}");
    QRegExpValidator* quote_validator = new QRegExpValidator(regQuote, this);
    edtAuditNum_->setValidator(quote_validator);
    QPushButton* btn_query = new QPushButton(tr("查询"));
    btn_query->setObjectName("auditQuery");

    tableView_ = new QTableView();
    model_ = new QStandardItemModel();
    tableView_->setModel(model_);

    QHeaderView *headerGoods = tableView_->horizontalHeader();
    //SortIndicator为水平标题栏文字旁边的三角指示器
    headerGoods->setSortIndicator(0, Qt::AscendingOrder);
    headerGoods->setSortIndicatorShown(true);
    connect(headerGoods, SIGNAL(sectionClicked(int)),
            tableView_, SLOT (sortByColumn(int)));


    // model 初始化
    model_->setColumnCount(TABLE_COUNT);
    model_->setHeaderData(TABLE_IDX, Qt::Horizontal, tr("序号"));
    model_->setHeaderData(TABLE_AUDIT_ID, Qt::Horizontal, tr("审核编号"));
    model_->setHeaderData(TABLE_STATUS, Qt::Horizontal, tr("审核状态"));
    model_->setHeaderData(TABLE_SUM, Qt::Horizontal, tr("含交易数"));
    model_->setHeaderData(TABLE_OPERATION, Qt::Horizontal, tr("操作"));

    // tableview 初始化
    tableView_->setSelectionBehavior(QAbstractItemView::SelectRows); // 选中整行
    tableView_->setSelectionMode(QAbstractItemView::SelectionMode::SingleSelection); // 只能单选
    tableView_->setEditTriggers(QTableView::EditTrigger::NoEditTriggers); // 不能编辑
    tableView_->setAlternatingRowColors(true);
    tableView_->setMouseTracking(true);

//    tableView_->setColumnWidth(TABLE_IDX, 100);
//    tableView_->setColumnWidth(TABLE_STATUS, 80);
    tableView_->setColumnWidth(TABLE_SUM, 80);
//    tableView_->setColumnWidth(TABLE_OPERATION, 130);
    tableView_->horizontalHeader()->setSectionResizeMode(TABLE_IDX,QHeaderView::Interactive);
    tableView_->horizontalHeader()->setSectionResizeMode(TABLE_AUDIT_ID,QHeaderView::Stretch);
    tableView_->horizontalHeader()->setSectionResizeMode(TABLE_STATUS,QHeaderView::Stretch);
    tableView_->horizontalHeader()->setSectionResizeMode(TABLE_SUM,QHeaderView::Interactive);
    tableView_->horizontalHeader()->setSectionResizeMode(TABLE_OPERATION,QHeaderView::Stretch);



    // 信号链接
    connect(this, &SRWithdrawCashAuditWidget::sigBrief,
            this, [=](QString msg){
        OEMessageBox::BriefMessage(this,msg);
    });
    connect(btn_query, &QPushButton::clicked,
            this, &SRWithdrawCashAuditWidget::onQuery);
    connect(btnAll_, &QPushButton::clicked,
            this, [=] {
        if (model_->rowCount() == 0) {
            return;
        }
        if (btnAll_->text() == "全  选") {
            for (int i = 0; i < model_->rowCount(); ++i)
                model_->item(i)->setCheckState(Qt::CheckState::Checked);
            btnAll_->setText("取消全选");
        }
        else {
            for (int i = 0; i < model_->rowCount(); ++i)
                model_->item(i)->setCheckState(Qt::CheckState::Unchecked);
            btnAll_->setText("全  选");
        }

    });
    connect(btn_import,&QPushButton::clicked,
            this, &SRWithdrawCashAuditWidget::onImportXlsClick);
    connect(btn_signature, &QPushButton::clicked,
            this, &SRWithdrawCashAuditWidget::onBashSignatureClick);
    connect(btn_export, &QPushButton::clicked,
            this, &SRWithdrawCashAuditWidget::onExportXlsClick);
    connect(this, &SRWithdrawCashAuditWidget::sigSignStatus,
            this, &SRWithdrawCashAuditWidget::onTableSignModeChange);


    // Excel导入消息
    OENotifyManager *manager = new OENotifyManager();
    connect(this, &SRWithdrawCashAuditWidget::sigExcelDone,
            manager, [manager,this](int a, int b){
        QString title = "Excel导入非法";
        QString msg = QString("交易数据(条): %1  审核总数(笔): %2")
                            .arg(a).arg(b);
        if (a == -1 && b == -1) {
            msg = QString("导入文件非法!");
            SRDatabase::DB()->writeOperateLog("导入非法文件,请予以重视!", 10);
        }
        else if (a == -2 && b == -2) {
            msg = QString("导入文件非法!");
            SRDatabase::DB()->writeOperateLog("文件被篡改,请予以重视!", 10);
        }
        else if (b == -1) {
            msg = QString("第%1条数据已有签名历史")
                    .arg(a);
        }
        else {
            title = "Excel导入成功";
        }
        bThreadReadExcel_ = false;

        manager->notify(title, msg, ":/lab/logo", "");
    });
    // 签名消息
    connect(this, &SRWithdrawCashAuditWidget::sigSignDone,
            manager, [manager](int a, int b){
        if (b > 0) {
            QString msg1 = QString("密码或信息错误: 失败%1条")
                                .arg(b);
            manager->notify("签名失败", msg1, ":/lab/logo", "");
        }
        QString msg = QString("签名成功: %1")
                            .arg(a);
        manager->notify("签名完毕", msg, ":/lab/logo", "");
    });

    connect(tableView_, &QTableView::doubleClicked,
            this, &SRWithdrawCashAuditWidget::onRowDoubleClicked);

    // 大小
    btn_import->setFixedSize(64,24);
    btn_signature->setFixedSize(84,24);
    btn_export->setFixedSize(64,24);
    btnAll_->setFixedSize(84,24);

    edtAuditNum_->setFixedSize(87,24);
    btn_query->setFixedSize(58,24);


    // 布局
    layout_query->addWidget(lab_audit_num,0,Qt::AlignLeft);
    layout_query->addWidget(edtAuditNum_,0,Qt::AlignLeft);
    layout_query->addWidget(btn_query,0,Qt::AlignLeft);

    layout_operator->addWidget(btnAll_,0,Qt::AlignRight);
    layout_operator->addWidget(btn_import,0,Qt::AlignRight);
    layout_operator->addWidget(btn_signature,0,Qt::AlignRight);
    layout_operator->addWidget(btn_export,0,Qt::AlignRight);

    layout_navigation->addLayout(layout_query);
    layout_navigation->addStretch();
    layout_navigation->addLayout(layout_operator);

    layout_bottom->addWidget(labAuditCount_, 0, Qt::AlignLeft);
    layout_bottom->addWidget(labWalletName_, 0, Qt::AlignRight);

    layout->addLayout(layout_navigation);
    layout->addWidget(tableView_);
    layout->addLayout(layout_bottom);

    setLayout(layout);
}

bool SRWithdrawCashAuditWidget::isBusy()
{
    if (bThreadReadExcel_ || bThreadSign_)
        return true;
    return false;
}
void SRWithdrawCashAuditWidget::onImportXlsClick()
{
    // 检查当前是否有正在导入的Excel
    if (bThreadReadExcel_) {
        int ret = QMessageBox::question(this, "是否停止", "是否停止当前正在导入的Excel",
                                        QMessageBox::Yes|QMessageBox::No);
        if (ret == QMessageBox::Yes)
            onStopImportExcel();
        else
            return;
    }

    if (bThreadSign_) {
        int ret = QMessageBox::question(this, "是否停止", "是否停止当前正在进行的签名",
                                        QMessageBox::Yes|QMessageBox::No);
        if (ret == QMessageBox::Yes)
            bThreadSign_ = false;
        else
            return;
    }


    static QString last_open_dir = "..";
    QString file_name = QFileDialog::getOpenFileName(this, tr("打开待审核的Excecl表格"),
                                 last_open_dir, tr("Excel表格文件(*.xls)"));

    if (file_name.isEmpty())
        return;


#if 1 // 钱包自动识别
    OEExcelReader reader(file_name);

    int row_count = reader.getUsedRowsCount();
    int col_count = reader.getUsedColsCount();

    // 读取excel的公钥信息
    QString public_key = reader.getCellValue(row_count - 1, col_count - 2);
    if (public_key.isEmpty()) {
        emit sigBrief(QString("非法的Excel文件"));
        bThreadReadExcel_ = false;
        return;
    }
    // 加载本地钱包
    OEWalletManage walletInfo;
    walletInfo.clearWallet();
    int size = walletInfo.loadWallet(".");
    if (size == 0) {
        emit sigBrief(QString("加载钱包失败"));
        bThreadReadExcel_ = false;
        return;
    }


    // 检索与excel对应的钱包
    auto wallet = walletInfo.getWalletInfo(public_key);


    if (wallet.name.isEmpty()) {
        emit sigBrief(QString("没有可用于签名的钱包"));
        bThreadReadExcel_ = false;
        return;
    }
    else if (0 == QString::compare("隐藏",wallet.status)) {
        int ret = QMessageBox::question(Q_NULLPTR,tr("钱包已存在"),
                              tr("用于签名的钱包已被隐藏,是否取消隐藏？"),
                              QMessageBox::Yes|QMessageBox::No);
        if (ret != QMessageBox::Yes)
            return;

        const QString& config = wallet.absPath + "/wallet";
//        qDebug() << config;
        QSettings setting(config, QSettings::IniFormat);
        setting.setValue("INFO/status", "提币中");
    }


    seedPath_ = wallet.seedAbsPath;
    labWalletName_->setText(labWalletName_->objectName().arg(wallet.name));
    reader.closeWorkBook();

#endif

    std::thread t(threadReaderXls, this, file_name);
    t.detach();
}

void SRWithdrawCashAuditWidget::onExportXlsClick() {

    // 验证是否有选中的数据
    bool bflag = false;

    for (int i = 0; i < model_->rowCount(); ++i) {
        if (model_->item(i,0)->checkState()
                == Qt::CheckState::Checked) {
            bflag = true;
            if (model_->item(i,TABLE_STATUS)->text() != "签名成功") {
                OEMessageBox::BriefMessage(this,"只能导出已签名数据");
                return;
            }
        }
    }
    if (!bflag) {
        OEMessageBox::BriefMessage(this,"请选择交易信息");
        return;
    }

    // 检查当前是否有正在导入的Excel
    if (bThreadReadExcel_) {
        int ret = QMessageBox::question(this, "是否停止", "是否停止当前正在导入的Excel",
                                        QMessageBox::Yes|QMessageBox::No);
        if (ret == QMessageBox::Yes)
            onStopImportExcel();
        else
            return;
    }

    if (bThreadSign_) {
        int ret = QMessageBox::question(this, "是否停止", "是否停止当前正在进行的签名",
                                        QMessageBox::Yes|QMessageBox::No);
        if (ret == QMessageBox::Yes)
            bThreadSign_ = false;
        else
            return;
    }




    static QString last_open_dir = "..";
    QString file_name = QFileDialog::getSaveFileName(this, tr("打开待审核的Excecl表格"),
                                 last_open_dir, tr("Excel表格文件(*.xls)"));
    if (file_name.isEmpty()) {
        return ;
    }

    // @remark： 后面改成异步

    OEExcelWriter write;
    write.CreateWorkSheet("withdraw cash");

    int r = 1;
    write.SetCellText(0,0,"audit_id",true);
    write.SetCellText(0,1,"bitcoin_hex",true);
    write.SetCellText(0,2,"bitcoin",true);
    for (int i = 0; i < model_->rowCount(); ++i) {
        if (model_->item(i,0)->checkState()
                == Qt::CheckState::Checked) {
            QString audit_id = model_->item(i,TABLE_AUDIT_ID)->text();
            stSignData temp_sign_data = mapBroadcasting_.find(audit_id).value();
            write.SetCellText(r,0,audit_id.toUtf8().data(),false);
            write.SetCellText(r,1,temp_sign_data.hex.toUtf8().data(),false);
            write.SetCellText(r,2,temp_sign_data.data.toUtf8().data(),false);
            ++r;
            // 更新为已导出
            SRDatabase::DB()->upXlsRecordExport(audit_id);
            model_->item(i, TABLE_OPERATION)->setText("已导出");
        }
    }
    // 将导出的写入本地数据库

    // window下不能输出utf路径
#ifdef Q_OS_WIN32
    QTextCodec* pCodec = QTextCodec::codecForName("gbk");
    if(!pCodec)
        qWarning("codec convert gb2312 failed!");

    QByteArray arr = pCodec->fromUnicode(file_name);

    write.SaveExcelFile(arr.data());
#else
    write.SaveExcelFile(file_name.toUtf8().data());
#endif

}

void SRWithdrawCashAuditWidget::onBashSignatureClick() {

    // 检查当前是否有正在导入的Excel
    if (bThreadReadExcel_) {
        int ret = QMessageBox::question(this, "是否停止", "是否停止当前正在导入的Excel",
                                        QMessageBox::Yes|QMessageBox::No);
        if (ret == QMessageBox::Yes)
            onStopImportExcel();

        return;
    }

    if (bThreadSign_) {
        int ret = QMessageBox::question(this, "是否停止", "是否停止当前正在进行的签名",
                                        QMessageBox::Yes|QMessageBox::No);
        if (ret == QMessageBox::Yes)
            bThreadSign_ = false;

        return;
    }

//    for (QList<stWithdrawCash>::iterator test_iter = iter->begin();
//         test_iter != iter->end(); ++test_iter)
//        qDebug() << test_iter->info;


    const int& row_count = model_->rowCount();
    QMap<QString, QList<stWithdrawCash> > map_audit;
//    int signature_count = 0;
    for (int i = 0; i < row_count; ++i) {
        if (Qt::CheckState::Checked == model_->item(i)->checkState()) {
//            qDebug() << QString("签名第%1项").arg(model_->item(i)->text());
            // 这里做签名数据
            QString temp_audit_id = model_->item(i,TABLE_AUDIT_ID)->text();

#if 0 /// 历史签名验证
            if (0 == SRDatabase::DB()->checkXlsRecord(temp_audit_id)) {
                OEMessageBox::BriefMessage(this, "不能重复签名");
                return ;
            }
#endif

            QMap<QString, QList<stWithdrawCash> >::iterator iter = mapAudit_.find(temp_audit_id);
            if (iter == mapAudit_.end()) {
                qWarning("bash signature find error");
                continue;
            }
            map_audit.insert(temp_audit_id, iter.value());

        }
    }
    if (0 == map_audit.size()) {
        OEMessageBox::BriefMessage(this, tr("请选择签名项!"));
        return;
    }

    char cryptolalia_data[256] = {};
    char main_prv_key[256] = {};

    /// 这里请求用户输入密码
    OEInputBox* w = new OEInputBox;
    w->setEditLabel("复审员A 密码：");
    w->setEditEchoMode(QLineEdit::EchoMode::Password);
    w->setEditPlaceholderText("请输入6位的复审员密码");
    w->setEditValidator("[0-9]{1,6}");
    w->resize(400,300);
    w->setFocus();
    static QString pswd;
    pswd = "";
    connect(w,&OEInputBox::sigInputMsg,
            this,[=](const QString& super_pswd){
        if (super_pswd.isEmpty()) {
            OEMessageBox::BriefMessage(this, "密码不能为空");
            return;
        }
        pswd = super_pswd;

        OEInputBox* w = new OEInputBox;
        w->setEditEchoMode(QLineEdit::EchoMode::Password);
        w->setEditLabel("复审员B 密码：");
        w->setEditPlaceholderText("请输入6位的复审员密码");
        w->setEditValidator("[0-9]{1,6}");
        w->resize(400,300);
        connect(w,&OEInputBox::sigInputMsg,
                this,[=](const QString& super_pswd){
            if (super_pswd.isEmpty()) {
                OEMessageBox::BriefMessage(this, "密码不能为空");
                return;
            }
            pswd += super_pswd;
        });
        w->setFocus();
        w->exec();
    });
    w->exec();
    if (pswd.isEmpty())
        return;
    if (pswd.length() != 12) {
        OEMessageBox::BriefMessage(this, "密码格式不正确");
        return;
    }

#if 0
    QFile file("audit.sr.seed");
#else
    QFile file(seedPath_);
#endif
    if (!file.open(QFile::ReadOnly)) {
        qWarning("seed open failed, because open audit.sr.seed failed.");
        qWarning() << QDir::currentPath() << ":" << seedPath_;
        return;
    }
    char ch_seed[256] = {};
    file.read(ch_seed,sizeof(ch_seed));

    apiDecryptionCryptolalia(ch_seed, sizeof(ch_seed),
                             cryptolalia_data,pswd.toUtf8().data());


#ifdef QT_DEBUG
    qDebug() << "解密后的密语：" << QString::fromUtf8(cryptolalia_data);
#endif
    QString str = QString::fromUtf8(cryptolalia_data);


    QString pattern("^[\u4e00-\u9fa5]{12}$");
    QRegExp rx(pattern);

    bool match = rx.exactMatch(str);
    if (!match) {
        OEMessageBox::BriefMessage(this, tr("复审员密码错误"));
        return;
    }


#if 1 // 写死的约定测试私钥
    apiGeneratedMainPrvKey(cryptolalia_data,"2011/11/11",main_prv_key);

#ifdef QT_DEBUG
    qDebug() << main_prv_key;
#endif
#else
    memcpy(main_prv_key, "tprv8bbpt3kAC4bmHtvq9vinCC5M9gBjLZyStWX7ErdnuzSYUHsQYs8SkrLgrRtZewTgMMpFeRQRAtmzJcQhsDVoCzb32kZKBtkrPbuHiKhTYQs", sizeof("tprv8bbpt3kAC4bmHtvq9vinCC5M9gBjLZyStWX7ErdnuzSYUHsQYs8SkrLgrRtZewTgMMpFeRQRAtmzJcQhsDVoCzb32kZKBtkrPbuHiKhTYQs"));
#endif
    std::thread t(threadSign,this, main_prv_key, map_audit);
    t.detach();

}

void SRWithdrawCashAuditWidget::onTableSignModeChange(
                            QString audit_id, bool status)
{
    QList<QStandardItem*> list_item =
            model_->findItems(audit_id,Qt::MatchExactly,1);
    if (list_item.size() != 1) {
        qWarning() << "audit id happen warning, audit id :"
                   << list_item.size()
                   << "(onTableSignModeChange.list_item)";
    }


    QStandardItem* operation_item = Q_NULLPTR;
    operation_item = new QStandardItem(tr("已操作"));

    if (status) {
        model_->setItem(list_item.first()->row(), TABLE_STATUS, new QStandardItem(tr("签名成功")));
        model_->item(list_item.first()->row(), TABLE_STATUS)->setTextAlignment(Qt::AlignCenter);
    }
    else{
        model_->setItem(list_item.first()->row(), TABLE_STATUS, new QStandardItem(tr("签名失败")));
        model_->item(list_item.first()->row(), TABLE_STATUS)->setTextAlignment(Qt::AlignCenter);
    }

    model_->setItem(list_item.first()->row(), TABLE_OPERATION, operation_item);
    model_->item(list_item.first()->row(), TABLE_OPERATION)->setTextAlignment(Qt::AlignCenter);

    if (!status) {
        for (int i = 0; i < TABLE_COUNT; ++i)
            model_->item(list_item.first()->row(),i)->setForeground(QBrush(QColor(255,0,0)));
    }
    else {
        for (int i = 0; i < TABLE_COUNT; ++i)
            model_->item(list_item.first()->row(),i)->setForeground(QBrush(QColor(0,0,0)));
    }
    update();
}

void SRWithdrawCashAuditWidget::onStopSign()
{
    bThreadSign_ = false;
}

void SRWithdrawCashAuditWidget::onStopImportExcel()
{
    bThreadReadExcel_ = false;
    int ret_row = model_->rowCount();
    if (ret_row > 0) {
        QTimer::singleShot(1000,[=]{
            mapAudit_.remove(model_->item((ret_row -1),TABLE_AUDIT_ID)->text());
            model_->removeRow(ret_row - 1);
        });
    }
}

void SRWithdrawCashAuditWidget::onRowDoubleClicked(const QModelIndex &_item)
{
    pageAuditInfo_->setAuditId(model_->item(_item.row(),TABLE_AUDIT_ID)->text());

    QMap<QString, QList<stWithdrawCash> >::iterator audit_data =
            mapAudit_.find(model_->item(_item.row(), TABLE_AUDIT_ID)->text());

    if (audit_data != mapAudit_.end())
        pageAuditInfo_->setViewData(audit_data.value());

    pageAuditInfo_->show();
    pageAuditInfo_->raise();
    pageAuditInfo_->setFocus();
}


void SRWithdrawCashAuditWidget::threadReaderXls(QString filename) {
    bThreadReadExcel_ = true;

    // 清空model数据
    model_->removeRows(0,model_->rowCount());
    mapAudit_.clear();
    mapBroadcasting_.clear();
    btnAll_->setText("全  选");


    OEExcelReader reader(filename);

    int row_count = reader.getUsedRowsCount();
    int col_count = reader.getUsedColsCount();

#if 0 // 钱包自动识别

    // 读取excel的公钥信息
    QString public_key = reader.getCellValue(row_count - 1, col_count - 2);
    if (public_key.isEmpty()) {
        emit sigBrief(QString("非法的Excel文件"));
        bThreadReadExcel_ = false;
        return;
    }
    // 加载本地钱包
    OEWalletManage walletInfo;
    walletInfo.clearWallet();
    int size = walletInfo.loadWallet(".");
    if (size == 0) {
        emit sigBrief(QString("加载钱包失败"));
        bThreadReadExcel_ = false;
        return;
    }


    // 检索与excel对应的钱包
    auto wallet = walletInfo.getWalletInfo(public_key);


    if (wallet.name.isEmpty()) {
        emit sigBrief(QString("没有可用于签名的钱包"));
        bThreadReadExcel_ = false;
        return;
    }


    seedPath_ = wallet.seedAbsPath;
    labWalletName_->setText(labWalletName_->objectName().arg(wallet.name));
#endif




#if 1  // 列文件防篡改验证
    QString md5 = reader.getCellValue(row_count - 1, col_count - 1);
    // 最后一行是防篡改验证码
    --row_count;

    QString md5_sum = "SR,";
    for (int r = 2, c = col_count - 1; r < row_count; ++r) {
        md5_sum += reader.getCellValue(r,c);
        md5_sum += ",";
        if (bThreadReadExcel_ == false) {
            reader.closeWorkBook();
            return;
        }
        else if (r % 1000 == 0) {
            emit sigBrief(QString("防篡改验证中: %1").arg(r - 2));
        }
    }

    char ch_record_md5[64] = {};
    apiStdHashData(md5_sum.toUtf8().data(),(unsigned char*)ch_record_md5);
    if (0 != strcmp(ch_record_md5, md5.toUtf8().data())) {
        emit this->sigExcelDone(-2, -2);
        reader.closeWorkBook();
        return ;
    }
#endif

    if (col_count < TABLE_COUNT) {
        qWarning() << "contract Six columns, but excel columns data under 6.";

    }

    if (row_count == 0 || col_count <= 6) {
        emit this->sigExcelDone(-1, -1);
        bThreadReadExcel_ = false;
        mapAudit_.clear();
        model_->removeRows(0,model_->rowCount());
        reader.closeWorkBook();
        return ;
    }

    // excel read all
    int r = 2;
    for (; r < row_count; r++) {
        if (!bThreadReadExcel_) {
            break;
        }
        stWithdrawCash temp_line;
        temp_line.prv_idx = reader.getCellValue(r,0);
        temp_line.source_addr = reader.getCellValue(r,1);
        temp_line.dest_addr = reader.getCellValue(r,2);
        temp_line.dest_money = reader.getCellValue(r,3);
        temp_line.tip = reader.getCellValue(r,4);
        temp_line.is_verify = reader.getCellValue(r,5);
        temp_line.info = reader.getCellValue(r,6);
        QString audit_id= reader.getCellValue(r,7);
        QString md5 = reader.getCellValue(r,8);

#if 1 /// 行防篡改验证

        QString record = QString("SR,%1,%2,%3,%4,%5,%6,%7,%8")
                                .arg(temp_line.prv_idx).arg(temp_line.source_addr)
                                .arg(temp_line.dest_addr).arg(temp_line.dest_money)
                                .arg(temp_line.tip).arg(temp_line.is_verify)
                                .arg(temp_line.info).arg(audit_id);

        char ch_record_md5[64] = {};
        apiStdHashData(record.toUtf8().data(),(unsigned char*)ch_record_md5);

        if (temp_line.prv_idx.isEmpty()
            || temp_line.source_addr.isEmpty()
            || temp_line.dest_addr.isEmpty()
            || temp_line.dest_money.isEmpty()
            || temp_line.tip.isEmpty()
            || temp_line.is_verify.isEmpty()
            || audit_id.isEmpty()
            || md5.isEmpty()
            || 0 != strcmp(ch_record_md5, md5.toUtf8().data())) {

            /// [1] 生成excel路径
            QString file_path = QString("storages/warning");
            QDir dir(file_path);
            if(!dir.exists()) {
                dir.mkpath(file_path);
            }
            QString file_name = QString("%1/%2")
                    .arg(file_path)
                    .arg(QDateTime::currentDateTime().toString("yyyyMMddHHmmsszzz"));


            /// [2] 把文件写入本地
            QFile file(file_name);
            if (file.open(QIODevice::Truncate
                      | QIODevice::WriteOnly)) {
                QTextStream in(&file);
                in << record.mid(3) << "," << md5;
                file.close();
            }

            emit this->sigExcelDone(-1, -1);
            bThreadReadExcel_ = false;
            mapAudit_.clear();
            model_->removeRows(0,model_->rowCount());
            reader.closeWorkBook();
            return ;
        }
#endif





        // last transaction info analysis  - json
        {
            QJsonDocument json_doc = QJsonDocument::fromJson(temp_line.info.toUtf8());
            QJsonArray json_arr = json_doc.array();
            for (int i = 0; i < json_arr.size(); ++i) {
                QJsonObject json_obj = json_arr.at(i).toObject();

                stTransaction temp_tr;
                temp_tr.txid += json_obj.value("txid").toString("error");
                temp_tr.vout += json_obj.value("vout").toString("0");
                temp_tr.money += json_obj.value("amount").toString("0");
                temp_line.last_tr.push_back(temp_tr);
            }
        }


        // insert audit list
        auto iter = mapAudit_.find(audit_id);
        if (iter == mapAudit_.end()) {
#if 0 /// 历史签名验证
            if (0 == SRDatabase::DB()->checkXlsRecord(audit_id)) {
                emit this->sigExcelDone(r-1, -1);
                bThreadReadExcel_ = false;
                return ;
            }
#endif

            mapAudit_.insert(audit_id,{temp_line});

        }
        else {
            iter->push_back(temp_line);
        }

        insertModelData(audit_id, temp_line);
    }
    reader.closeWorkBook();
    emit this->sigExcelDone(r-2, mapAudit_.count());
    bThreadReadExcel_ = false;
}

void SRWithdrawCashAuditWidget::threadSign(QString _mainPrvKey,
        QMap<QString, QList<stWithdrawCash> > _mapAudit)
{
    bThreadSign_ = true;


    SRDatabase* p_db = SRDatabase::DB();
    // 签名
    int success = 0;
    for (QMap< QString,QList<stWithdrawCash> >::iterator iter = _mapAudit.begin();
         iter != _mapAudit.end(); ++iter) {
        QString transaction_exe = "auditd.exe";
        QString transaction_param_1;
        QString transaction_param_2;
        QString transaction_param_3 = iter.key();
        // last transaction
        for (QList<stWithdrawCash>::iterator item = iter.value().begin();
             item != iter.value().end(); item++) {
            for (QList<stTransaction>::iterator lasttr_iter = item->last_tr.begin();
                 lasttr_iter != item->last_tr.end();
                 ++lasttr_iter) {

                if (!bThreadSign_)
                    return;


                transaction_param_1 += lasttr_iter->txid;
                transaction_param_1 += ",";
                transaction_param_1 += lasttr_iter->vout;
                transaction_param_1 += ",";
                transaction_param_1 += lasttr_iter->money;
                transaction_param_1 += ",";
                transaction_param_1 += item->source_addr;
                transaction_param_1 += ",";

                // 推导prvKey
                char mainPrvKey[256] = {};
                char prvKey[256] = {};

                int i = item->prv_idx.indexOf("/");
                if (i != -1)
                    i = item->prv_idx.indexOf("/",i+1);

                QString temp_prv_idx = item->prv_idx;
                if (i != -1) {
                    temp_prv_idx = item->prv_idx.mid(i+1);
                }
                apiGeneratedChildPrvKey(_mainPrvKey.toUtf8().data(),
                                        temp_prv_idx.toUtf8().data(),
                                        mainPrvKey);

                apiGeneratedPrvKeyInfo(mainPrvKey,Q_NULLPTR,
                                       Q_NULLPTR, prvKey, Q_NULLPTR);

                if (mainPrvKey[0] == '\0') {
                    qWarning("generated child private key failed");
                }
                transaction_param_1 += prvKey;
                transaction_param_1 += ";";
            }
        }
#ifdef QT_BUDEG
        qDebug() << "transaction param 1 :\n\t" << transaction_param_1 << "\n";
#endif


        for (auto item : iter.value()) {
            transaction_param_2 += item.dest_addr;
            transaction_param_2 += ",";
            transaction_param_2 += item.dest_money;
            transaction_param_2 += ";";
        }

        // 执行外部程序
        QProcess process;
        QStringList cmd_args;


        cmd_args << transaction_param_1 << transaction_param_2 << transaction_param_3 << BITCOIN_TYPE.c_str();
#ifdef QT_DEBUG // 传入的参数
        qDebug() << "cmd_args param  :\n\t" << cmd_args << "\n";
#endif
        process.start(transaction_exe, cmd_args);
        process.waitForStarted();
        process.waitForFinished();
        QString strTemp=QString::fromLocal8Bit(process.readAllStandardOutput());

        // 检验签名成功
        int ret_pos = strTemp.indexOf("app end:");

        QString log = QString("审核编号（%1）正在进行签名：").arg(iter.key());
        int strTemp_length = strTemp.length();
        if (ret_pos>0)
            ret_pos = strTemp_length - ret_pos;

        if (ret_pos > 20) {
            success++;
            saveSuccessTransaction(iter.key(), iter.value());
            // 广播数据
            QString ret_data = strTemp.split("app end:").last();
            int tail_pos = ret_data.indexOf("]");
            if (tail_pos > 0) {
                ret_data = ret_data.mid(0,tail_pos);
            }

            // 可视化数据
            QString ret_transaction_data = strTemp.split("[transaction mtx decode]:").last();


            tail_pos = ret_transaction_data.indexOf("[/transaction mtx decode]");
            if (tail_pos > 0)
                ret_transaction_data = ret_transaction_data.mid(0,tail_pos);

            mapBroadcasting_.insert(transaction_param_3,
                                    stSignData(ret_transaction_data,ret_data));
            emit this->sigSignStatus(transaction_param_3, true);
            log += "签名成功";

            p_db->writeXlsRecord(SRDatabase::stXlsRecord(
                                 ret_data, transaction_param_3,
                                     ret_transaction_data));
        }
        else {
            emit this->sigSignStatus(transaction_param_3, false);
            log += "签名失败";
//#ifndef QT_DEBUG
            qWarning()  << "签名失败：" << strTemp;
//#endif
        }
        if (p_db) {
            p_db->writeOperateLog(log,1);
        }
    }



    emit this->sigSignDone(success, (_mapAudit.count() - success));
    bThreadSign_ = false;
}


void SRWithdrawCashAuditWidget::saveSuccessTransaction(const QString &_auditId,
                                                       QList<stWithdrawCash> &_transaction)
{
    OEExcelWriter write;
    write.CreateWorkSheet(_auditId.toUtf8().data());

    // 标题行
    int row = 0;
    write.SetCellText(row,0,"transaction_size",true);
//    write.SetCellText(row,1,"transaction_tip",true);
//    write.SetCellText(row,2,"vin_sum_money",true);
//    write.SetCellText(row,3,"vout_sum_money",true);

    // 标题参数行
    ++row;
    write.SetCellText(row,0,QString::number(_transaction.size()).toUtf8().data(),false);
//    write.SetCellText(row,1,_transaction.last().tip.toUtf8().data(),false);
    // 计算总输入与总输出
//    write.SetCellText(row,2,QString::number(_transaction.size()).toUtf8().data(),false);
//    write.SetCellText(row,3,QString::number(_transaction.size()).toUtf8().data(),false);


    // 主题内容标题行
    ++row;
    write.SetCellText(row,0,"vin_addr",true);
    write.SetCellText(row,1,"vin_money",true);
    write.SetCellText(row,2,"vout_addr",true);
    write.SetCellText(row,3,"vout_money",true);


    ++row;
    int vout_row = row;
    int vin_row = row;

    for (QList<stWithdrawCash>::iterator tr_iter = _transaction.begin();
         tr_iter != _transaction.end(); ++tr_iter) {
        for (QList<stTransaction>::iterator last_tr_iter = tr_iter->last_tr.begin();
             last_tr_iter != tr_iter->last_tr.end(); ++last_tr_iter) {
            write.SetCellText(vin_row,0,tr_iter->source_addr.toUtf8().data(),false);
            write.SetCellText(vin_row,1,last_tr_iter->money.toUtf8().data(),false);
            vin_row++;
        }

    }


    for (QList<stWithdrawCash>::iterator tr_iter = _transaction.begin();
         tr_iter != _transaction.end(); ++tr_iter) {
        write.SetCellText(vout_row, 2, tr_iter->dest_addr.toUtf8().data(),false);
        write.SetCellText(vout_row, 3, tr_iter->dest_money.toUtf8().data(),false);
        vout_row++;
    }




    // 保存
    QString file_path = "storages/transaction_record/";
    QString file_name = file_path + _auditId + ".xls";
    QDir(file_path).mkpath(file_path);

    if (QFile(file_name).exists()) {
        QString new_name = file_name + QTime::currentTime().toString("HHmmsszzz");
        QFile::rename(file_name,new_name);
    }

    // window下不能输出utf路径
#ifdef Q_OS_WIN32
    QTextCodec* pCodec = QTextCodec::codecForName("gbk");
    if(!pCodec)
        qWarning("codec convert gb2312 failed!");

    QByteArray arr = pCodec->fromUnicode(file_name);

    write.SaveExcelFile(arr.data());
#else
    write.SaveExcelFile(file_name.toUtf8().data());
#endif
}


void SRWithdrawCashAuditWidget::onQuery() {

    // @remakr : pan
    if (model_->rowCount() == 0) {
        return;
    }
    QString choose_txt = edtAuditNum_->text().trimmed();

    if (!choose_txt.isEmpty()) {
        QList<QStandardItem*> temp_list = model_->findItems(
                   choose_txt, Qt::MatchContains, TABLE_AUDIT_ID);

        QList<QStandardItem*>::iterator item = temp_list.begin();
        for (;item != temp_list.end(); ++item) {
            int old = tableView_->currentIndex().row();
            if ((*item)->row() > old) {
                tableView_->selectRow((*item)->row());
                break;
            }
        }
        if (item == temp_list.end()) {
            if (temp_list.size() != 0)
                tableView_->selectRow(temp_list.at(0)->row());
        }
    }
}

void SRWithdrawCashAuditWidget::insertModelData(QString audit_id,
                                             const stWithdrawCash &_data) {
    // 当前行数
    int idx = model_->rowCount();
    labAuditCount_->setText(labAuditCount_->objectName().arg(idx));

    QList<QStandardItem*> list_item =
            model_->findItems(audit_id,Qt::MatchExactly,1);


    if (list_item.isEmpty()) {
        // @bug: 这里有未知的警告
        //        QObject::connect: Cannot queue arguments of type 'QList<QPersistentModelIndex>'
        //        (Make sure 'QList<QPersistentModelIndex>' is registered using qRegisterMetaType().)
        //        QObject::connect: Cannot queue arguments of type 'QList<QPersistentModelIndex>'
        //        (Make sure 'QList<QPersistentModelIndex>' is registered using qRegisterMetaType().)
        //        QObject::connect: Cannot queue arguments of type 'QVector<int>'
        //        (Make sure 'QVector<int>' is registered using qRegisterMetaType().


        QStandardItem *id_item = new QStandardItem();
        id_item->setCheckable(true);
        id_item->setText(QString::number(idx + 1));
        id_item->setCheckState(Qt::Unchecked);
        model_->setItem(idx, TABLE_IDX, id_item);
        model_->setItem(idx, TABLE_AUDIT_ID, new QStandardItem(audit_id));
        model_->setItem(idx, TABLE_STATUS, new QStandardItem(_data.state));
        model_->setItem(idx, TABLE_SUM, new QStandardItem(QString("1")));
        model_->setItem(idx, TABLE_OPERATION, new QStandardItem(QString("待操作")));
        for (int i = 0 ; i < TABLE_COUNT; ++i) {
            model_->item(idx, i)->setTextAlignment(Qt::AlignCenter);
        }
    }
    else {
        for (auto item :list_item) {
            int num = model_->item(item->row(),TABLE_SUM)->text().toInt();
            ++num;
            QStandardItem *num_item = new QStandardItem();
            num_item->setText(QString::number(num));
            model_->setItem(item->row(),TABLE_SUM,num_item);
            model_->item(item->row(), TABLE_SUM)->setTextAlignment(Qt::AlignCenter);
            update();
        }
    }
}
